/*****************************************************************************
 * @brief 创建串口功能
 *****************************************************************************/
#include "stm32h5xx_ll_usart.h"

#ifndef qc_obj
#include "bsp.h"
#include "pincfg.h"
#include "mbapp.h"
#include "com_def.h"

/*****************************************************************************
 * @brief 串口信息
 *****************************************************************************/
#define qc_rx_enable()
#define qc_tx_enable()
#define RS485DE_CTR_IN_TIMER 0

qc_obj_type qc_obj;
#define qc_port USART3
#define qc_uart_irqn USART3_IRQn
//#define DMA_COM_RX DMA_U01_RX
//#define DMA_COM_TX DMA_U01_TX

static void qc_cb_set(void)
{
}
static void qc_port_hal_cfg(void) {}
#endif

/*****************************************************************************
 * @brief hal api
 *****************************************************************************/
static void qc_cb_set(void);
static void qc_tx1_rx0_enable(uint8_t mode);
static void qc_port_Init(uu8 mbMode, uint32_t ulBaudRate, mb_parity_type eParity);
static void qc_port_send(void);
static void qc_port_hal_cfg(void);
static void (*qc_byte_send)(uint8_t dat);

#if (defined DMA_COM_RX)
static void com_dma_rx_init(void);
static void com_dma_rx_restart(void);
#endif

#if (defined DMA_COM_TX)
static void com_dma_tx_init(void);
static void com_dma_tx_send(void);
static void com_tx_dma_irq(void);
#endif

/*****************************************************************************
 * @brief UART OPT
 *****************************************************************************/
// 数据收发
#define uart_send_data(dat) qc_port->TDR = dat
#define uart_receive_data() qc_port->RDR

// 缓冲状态
#define uart_rx_not_empty_st() ((qc_port->ISR & USART_ISR_RXNE) != 0) // rx not empty
#define uart_tx_not_full_st() (0 != (qc_port->ISR & USART_ISR_TXE))	// tx not full
#define uart_tx_comp_st() (qc_port->ISR & (USART_ISR_TC))
#define uart_tx_tc_clear() qc_port->ISR &= (~USART_ISR_TC)

// 接收控制
#define uart_rx_enable() qc_port->CR1 |= (USART_CR1_UE | USART_CR1_RE)
#define uart_rx_int_enable() qc_port->CR1 |= USART_CR1_RXNEIE
#define uart_rx_dma_enable() qc_port->CR3 |= USART_CR3_DMAR
#define uart_rx_int_disable()                           \
	qc_port->CR1 &= ~(USART_CR1_RXNEIE | USART_CR1_RE); \
	qc_port->CR3 &= ~USART_CR3_DMAR

// 发送控制
#define uart_tx_enable() qc_port->CR1 |= (USART_CR1_UE | USART_CR1_TE)

#define uart_tx_tde_enable() cset_mask(qc_port->CR1, USART_CR1_TCIE, USART_CR1_TXEIE)
#define uart_tx_dma_enable()                                  \
	clr_mask(qc_port->CR1, USART_CR1_TCIE | USART_CR1_TXEIE); \
	set_mask(qc_port->CR3, USART_CR3_DMAT)
#define uart_tx_tc_enable() cset_mask(qc_port->CR1, USART_CR1_TXEIE, USART_CR1_TCIE)
#define uart_tx_int_disable()                                 \
	clr_mask(qc_port->CR1, USART_CR1_TCIE | USART_CR1_TXEIE); \
	clr_mask(qc_port->CR3, USART_CR3_DMAT)

// 停止位
#define uart_stop_1bits_set() qc_port->CR2 &= ~USART_CR2_STOP_Msk
#define uart_stop_2bits_set() qc_port->CR2 |= LL_USART_STOPBITS_2

// 错误标志及清除
#define UART_RX_ERR_FLAG (USART_ISR_PE | USART_ISR_ORE)
#define uart_rx_error_clear()

/*****************************************************************************
 * @brief mb_byte_send
 * @param   dat
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void qc_rtu_byte_send(uint8_t dat)
{
	uart_send_data(dat);
}

/*****************************************************************************
 * @brief UART interrupt routine.
 * @param   none
 * @return  none
 *****************************************************************************/
void qc_isr()
{
	uint32_t IntSt, RegCR;
	volatile uint8_t Data;

	IntSt = qc_port->ISR;
	RegCR = qc_port->CR1;

	// 中断方式接收数据
	if ((IntSt & (USART_ISR_RXNE | USART_ISR_ORE)) && (RegCR & USART_CR1_RXNEIE))
	{
		Data = uart_receive_data();

		if ((IntSt & UART_RX_ERR_FLAG) != 0)
		{
			if (qc_obj.rx_cnt >= 1)
			{
				qc_obj.err_hal = 1;
			}
			uart_rx_error_clear();
		}

		qc_data_rcv(&qc_obj, Data);
	}
	// 中断方式发送数据 和 DMA发送结束
	else if ((IntSt & USART_ISR_TC) && (RegCR & USART_CR1_TCIE))
	{
		qc_send_end(&qc_obj);
		qc_tx1_rx0_enable(0);
		uart_tx_tc_clear();
	}
	// 中断方式发送数据
	else if ((IntSt & USART_ISR_TXE) && (RegCR & USART_CR1_TXEIE))
	{
		if (qc_data_send(&qc_obj, qc_rtu_byte_send, 1))
		{
			uart_tx_tc_enable();
		}
	}
	else
	{
	}
	// 对于DMA方式接收数据，定时中断中通过DMA变化和串口IDLE状态判定接收过程
	// 对于DMA方式发送数据，DMA发送中断中开启串口TC中断
}

/*****************************************************************************
 * @brief   	com modle init .
 * @param   none
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void qc_port_Init(uu8 qcMode, uint32_t ulBaudRate, mb_parity_type eParity)
{
	LL_USART_InitTypeDef cfg;

	qc_port_hal_cfg();

// 关闭 DMA
#if (defined DMA_COM_RX)
	dma_nvic_set(&DMA_COM_RX, 0, 0);
	dma_stream_deinit(&DMA_COM_RX);
	dma_stream_disable(&DMA_COM_RX);
#endif

#if (defined DMA_COM_TX)
	dma_nvic_set(&DMA_COM_TX, 0, 0);
	dma_stream_deinit(&DMA_COM_TX);
	dma_stream_disable(&DMA_COM_TX);
#endif

	qc_cb_set();
	qc_obj.mode = qcMode;
	qc_obj.dat_send = qc_port_send;

	qc_byte_send = qc_rtu_byte_send;

	cfg.BaudRate = ulBaudRate;
	cfg.TransferDirection = LL_USART_DIRECTION_TX_RX;
	cfg.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
	cfg.OverSampling = LL_USART_OVERSAMPLING_16;

	// rtu mode
	if (MB_PAR_NONE == eParity)
	{
		cfg.Parity = LL_USART_PARITY_NONE;
		cfg.StopBits = LL_USART_STOPBITS_2;
		cfg.DataWidth = LL_USART_DATAWIDTH_8B;
	}
	else if (MB_PAR_NONE_1S == eParity)
	{
		cfg.Parity = LL_USART_PARITY_NONE;
		cfg.StopBits = LL_USART_STOPBITS_1;
		cfg.DataWidth = LL_USART_DATAWIDTH_8B;
	}
	else if (MB_PAR_ODD == eParity)
	{
		cfg.Parity = LL_USART_PARITY_ODD;
		cfg.StopBits = LL_USART_STOPBITS_1;
		cfg.DataWidth = LL_USART_DATAWIDTH_9B;
	}
	else
	{
		cfg.Parity = LL_USART_PARITY_EVEN;
		cfg.StopBits = LL_USART_STOPBITS_1;
		cfg.DataWidth = LL_USART_DATAWIDTH_9B;
	}

	// rtu timer
	if (ulBaudRate > 19200)
	{
		qc_obj.tim_sv = 3;
	}
	else
	{
		qc_obj.tim_sv = (com_timer_cal(ulBaudRate, (35 * 11 + 9) / 10, 300, 12000) + 99) / 100;
	}

	LL_USART_Init(qc_port, &cfg);
	LL_USART_Enable(qc_port);

	nvic_irq_set(qc_uart_irqn, 0x06, 1);

#if (defined DMA_COM_RX)
	com_dma_rx_init();
#endif

#if (defined DMA_COM_TX)
	com_dma_tx_init();
#endif

	qc_tx1_rx0_enable(0);

	uart_tx_enable();
}

#if (defined DMA_COM_RX)
/*****************************************************************************
 * @brief   dma 接收初始化
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_rx_init(void)
{
	dma_stream_cfg_type cfg;

	// 配置参数
	cfg.cycle_mode = 0;
	cfg.mem_to_periph = 0;
	cfg.prio = LL_DMA_PRIORITY_MEDIUM;

	cfg.mem = qc_obj.buff;
	cfg.periph = &qc_port->DR;
	cfg.data_len = sizeof(qc_obj.buff);
	cfg.mem_inc = EXT_TRUE;
	cfg.periph_inc = EXT_FALSE;

	// 配置
	dma_stream_config(&DMA_COM_RX, &cfg);
	dma_stream_enable(&DMA_COM_RX);
}

/*****************************************************************************
 * @brief   dma 接收重置
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_rx_restart(void)
{
	intx_alloc();

	intx_disable();

	// 重置DMA计数器
	uart_rx_dma_enable();
	dma_stream_disable(&DMA_COM_RX); // 手动停止会产生TC标志

	dma_stream_set_mem_addr(&DMA_COM_RX, (uu32)qc_obj.buff);
	dma_stream_set_data_len(&DMA_COM_RX, sizeof(qc_obj.buff));

	dma_stream_enable(&DMA_COM_RX);

	intx_enable();
}
#endif

#if (defined DMA_COM_TX)
/*****************************************************************************
 * @brief   dma 发送初始化
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_tx_init(void)
{
	dma_stream_cfg_type cfg;

	// 配置参数
	cfg.cycle_mode = 0;
	cfg.mem_to_periph = 1;
	cfg.prio = LL_DMA_PRIORITY_MEDIUM;

	cfg.mem = qc_obj.buff;
	cfg.periph = &qc_port->DR;
	cfg.data_len = sizeof(qc_obj.buff);
	cfg.mem_inc = EXT_TRUE;
	cfg.periph_inc = EXT_FALSE;

	// 配置
	dma_stream_config(&DMA_COM_TX, &cfg);
	dma_stream_complete_isr_enable(&DMA_COM_TX);
	dma_nvic_set(&DMA_COM_TX, 1, com_tx_dma_irq);
}

/*****************************************************************************
 * @brief   dma 发送中断
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_tx_dma_irq(void)
{
	// 清除标志 并关闭通道
	dma_stream_disable(&DMA_COM_TX);
	dma_comp_flag_clr(&DMA_COM_TX);

	// 开启发送结束中断
	qc_obj.tx_cnt = qc_obj.tx_size;
	uart_tx_tc_enable();
}

/*****************************************************************************
 * @brief   dma tx check: 该函数需要重入，所有操作在临界段内执行
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_tx_send(void)
{
	// 发送状态
	qc_obj.hal_ctr.st_dir_ctr_req = 0;

	// 开启DMA传输
	uart_tx_tc_clear(); // 必须在开始发送时清除该状态，DMA结束不可清除，否则由于中断抢占和延时可能错过TC中断
	uart_tx_dma_enable();

	dma_stream_set_mem_addr(&DMA_COM_TX, (uu32)qc_obj.buff);
	dma_stream_set_data_len(&DMA_COM_TX, qc_obj.tx_size);
	dma_stream_enable(&DMA_COM_TX);

	qc_obj.hal_ctr.timeout_cnt = 0;
	qc_obj.hal_ctr.timeout_set = (qc_obj.tim_sv * qc_obj.tx_size / 2);
}
#endif

/*****************************************************************************
 * @brief   timer handle.
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void qc_timer_run(void)
{
#if (defined DMA_COM_RX)
	uint16_t rx_site;
#endif
	qc_timer_handle(&qc_obj);

#if (defined DMA_COM_RX)
	// 对于DMA接收，状态变为IDLE时重置接收器
	if (qc_obj.hal_ctr.com_st_old != qc_obj.com_st)
	{
		if (qc_obj.com_st == QC_IDLE)
		{
			com_dma_rx_restart();
			qc_obj.rx_cnt = 0;
		}
		qc_obj.hal_ctr.com_st_old = qc_obj.com_st;
	}

	// 根据DMA计数器判定接收状态
	rx_site = dma_stream_get_data_len(&DMA_COM_RX);
	rx_site = sizeof(qc_obj.buff) - rx_site;
	if (rx_site && (rx_site != qc_obj.rx_cnt))
	{
		// 仅空闲、接收、等待状态允许接收数据
		if (QC_RX_RCV == qc_obj.com_st)
		{
			qc_obj.tim_cnt = 0;
			qc_obj.tout_cnt = 0;
			qc_obj.rx_cnt = rx_site;
		}
		else if ((QC_IDLE == qc_obj.com_st) || (QC_RX_WAIT == qc_obj.com_st))
		{
			qc_obj.com_st = QC_RX_RCV;

			qc_obj.tim_cnt = 0;
			qc_obj.tout_cnt = 0;
			qc_obj.rx_cnt = rx_site;
		}
	}

#endif

// DMA 发送超时强制结束
#if (defined DMA_COM_TX)
	if (is_dma_stream_enable(&DMA_COM_TX))
	{
		qc_obj.hal_ctr.timeout_cnt++;
		if (qc_obj.hal_ctr.timeout_cnt > qc_obj.hal_ctr.timeout_set)
		{
			com_tx_dma_irq();
			qc_tx1_rx0_enable(0);
		}
	}
#endif
}

/*****************************************************************************
 * @brief   Uart En or Dis.
 * @param   1 tx  0 rx
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/

static void qc_tx1_rx0_enable(uint8_t mode)
{
	volatile uint8_t u8Temp;

	// 开启接收
	if (0 == mode)
	{
		qc_rx_enable();

		uart_stop_1bits_set();

		uart_tx_int_disable();
		uart_rx_enable();

		u8Temp = qc_port->RDR;
		while ((qc_port->ISR & (USART_ISR_RXNE | USART_ISR_ORE)) != 0)
		{
			u8Temp = qc_port->RDR;
			__DSB();
		}

#if (defined DMA_COM_RX)
		com_dma_rx_restart();
#else
		uart_rx_int_enable();
#endif
	}
	// 开启发送
	else
	{
		qc_tx_enable();

		uart_stop_2bits_set();

		uart_rx_int_disable();

#if (defined DMA_COM_TX)
		com_dma_tx_send();
#else
		uart_tx_tde_enable();
#endif
	}
}

/*****************************************************************************
 * @brief   Send data.
 * @param   none
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void qc_port_send(void)
{
	qc_tx1_rx0_enable(1);
}

/*****************************************************************************
 * @brief   sim_mbport_keep
 * @param   none
 * @return  none
 *****************************************************************************/
#ifndef qc_obj
void qc_sim_mbport_keep(void)
{
	qc_port_Init(0, 9600, MB_PAR_NONE);
}
#endif